package com.ruoyi.system.websocket;

import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.ChatMessage;
import com.ruoyi.system.service.IChatMessageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * WebSocket聊天服务端点
 * 
 * @author ruoyi
 */
@ServerEndpoint("/websocket/chat/{token}")
@Component
public class WebSocketChatServer {

    private static final Logger log = LoggerFactory.getLogger(WebSocketChatServer.class);
    
    /**
     * 用于记录当前在线连接数
     */
    private static AtomicInteger onlineCount = new AtomicInteger(0);
    
    /**
     * 存储所有客户端连接，key为用户ID
     */
    private static Map<String, WebSocketChatServer> clients = new ConcurrentHashMap<>();
    
    /**
     * 心跳检测调度器
     */
    private static ScheduledExecutorService heartbeatScheduler = Executors.newSingleThreadScheduledExecutor();
    
    /**
     * 当前连接的会话
     */
    private Session session;
    
    /**
     * 用户ID
     */
    private String userId;
    
    /**
     * 最后活跃时间
     */
    private long lastActiveTime;
    
    /**
     * 注入ChatMessageService（需要特殊处理，因为WebSocket不是单例）
     */
    private static IChatMessageService chatMessageService;
    
    @Autowired
    public void setChatMessageService(IChatMessageService chatMessageService) {
        WebSocketChatServer.chatMessageService = chatMessageService;
    }
    
    /**
     * 静态初始化，启动心跳检测任务
     */
    static {
        // 每30秒执行一次心跳检测
        heartbeatScheduler.scheduleAtFixedRate(() -> {
            try {
                checkHeartbeat();
            } catch (Exception e) {
                log.error("心跳检测异常", e);
            }
        }, 30, 30, TimeUnit.SECONDS);
    }
    
    /**
     * 心跳检测任务
     */
    private static void checkHeartbeat() {
        long currentTime = System.currentTimeMillis();
        // 遍历所有连接，检查最后活跃时间
        clients.forEach((id, client) -> {
            // 如果超过60秒没有活动，发送心跳包
            if (currentTime - client.lastActiveTime > 60000) {
                try {
                    client.session.getBasicRemote().sendText("{\"type\":\"heartbeat\",\"timestamp\":"+currentTime+"}");
                    log.debug("发送心跳包给用户：{}", id);
                } catch (IOException e) {
                    log.error("发送心跳包失败，可能连接已断开", e);
                    // 移除失效的连接
                    clients.remove(id);
                    onlineCount.decrementAndGet();
                    log.info("心跳检测：移除断开的连接，用户ID：{}，当前在线人数：{}", id, onlineCount.get());
                }
            }
        });
    }
    
    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token) {
        this.session = session;
        
        // 获取用户信息
        LoginUser loginUser = getLoginUserFromToken(session, token);
        if (loginUser == null) {
            try {
                session.close();
            } catch (IOException e) {
                log.error("关闭无效会话异常", e);
            }
            return;
        }
        
        this.userId = loginUser.getUserId().toString();
        this.lastActiveTime = System.currentTimeMillis();
        
        // 添加到map中
        clients.put(userId, this);
        
        // 在线数加1
        onlineCount.incrementAndGet();
        
        log.info("用户连接：{}，当前在线人数为：{}", userId, onlineCount.get());
        
        // 向客户端发送连接成功消息
        try {
            this.session.getBasicRemote().sendText("{\"type\":\"connected\",\"userId\":\""+userId+"\"}");
        } catch (IOException e) {
            log.error("发送连接消息异常", e);
        }
    }
    
    /**
     * 从token中获取登录用户信息
     */
    private LoginUser getLoginUserFromToken(Session session, String token) {
        try {
            // 从Spring容器中获取TokenService
            Object tokenService = getTokenService(session);
            // 通过反射调用getLoginUser方法
            Method method = tokenService.getClass().getMethod("getLoginUser", String.class);
            return (LoginUser) method.invoke(tokenService, token);
        } catch (Exception e) {
            log.error("调用TokenService.getLoginUser方法异常", e);
            return null;
        }
    }
    
    /**
     * 从Spring容器中获取TokenService
     */
    private Object getTokenService(Session session) {
        EndpointConfig config = (EndpointConfig) session.getUserProperties().get("javax.websocket.endpoint.config");
        HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(httpSession.getServletContext());
        return context.getBean("tokenService");
    }
    
    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        if (userId != null) {
            // 从map中移除
            clients.remove(userId);
            // 在线数减1
            onlineCount.decrementAndGet();
            log.info("用户退出：{}，当前在线人数为：{}", userId, onlineCount.get());
        }
    }
    
    /**
     * 收到客户端消息后调用的方法
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("服务端收到用户{}的消息：{}", userId, message);
        
        // 更新最后活跃时间
        this.lastActiveTime = System.currentTimeMillis();
        
        try {
            // 解析消息
            Map<String, Object> msgMap = JSON.parseObject(message, Map.class);
            String type = (String) msgMap.get("type");
            
            // 处理不同类型的消息
            if ("heartbeat".equals(type)) {
                // 心跳消息，直接回复
                this.session.getBasicRemote().sendText("{\"type\":\"heartbeat_response\",\"timestamp\":"+System.currentTimeMillis()+"}");
                return;
            }
            
            if ("chat".equals(type)) {
                // 聊天消息，进行处理
                ChatMessage chatMessage = JSON.parseObject(message, ChatMessage.class);
                
                // 设置发送者ID
                chatMessage.setFromUserId(userId);
                
                // 设置消息状态为未读
                chatMessage.setReadStatus("0");
                
                // 设置发送状态为发送中
                chatMessage.setSendStatus("0");
                
                // 设置创建时间
                chatMessage.setCreateTime(new Date());
                
                // 保存消息到数据库
                saveChatMessage(chatMessage);
                
                // 发送消息给接收者
                sendMessageToUser(chatMessage.getToUserId(), JSON.toJSONString(chatMessage));
                
                // 更新消息状态为已发送
                updateMessageSendStatus(chatMessage.getId(), "1");
                
                // 如果接收者在线，则更新状态为已送达
                if (clients.containsKey(chatMessage.getToUserId())) {
                    updateMessageSendStatus(chatMessage.getId(), "2");
                }
            }
        } catch (Exception e) {
            log.error("处理消息异常", e);
        }
    }
    
    /**
     * 发生错误时调用
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("WebSocket发生错误：{}，Session ID：{}", error.getMessage(), session.getId());
        error.printStackTrace();
    }
    
    /**
     * 发送消息给指定用户
     */
    public static void sendMessageToUser(String toUserId, String message) {
        WebSocketChatServer client = clients.get(toUserId);
        if (client != null) {
            try {
                client.session.getBasicRemote().sendText(message);
                log.info("消息发送成功，接收用户：{}", toUserId);
            } catch (IOException e) {
                log.error("发送消息异常", e);
            }
        } else {
            log.info("用户{}不在线，消息将在其上线后推送", toUserId);
        }
    }
    
    /**
     * 保存聊天消息到数据库
     */
    private void saveChatMessage(ChatMessage chatMessage) {
        if (chatMessageService == null) {
            // 如果chatMessageService为空，则从Spring容器中获取
            EndpointConfig config = (EndpointConfig) session.getUserProperties().get("javax.websocket.endpoint.config");
            HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
            WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(httpSession.getServletContext());
            chatMessageService = context.getBean(IChatMessageService.class);
        }
        
        // 保存消息
        chatMessageService.insertChatMessage(chatMessage);
    }
    
    /**
     * 更新消息发送状态
     */
    private void updateMessageSendStatus(Long messageId, String sendStatus) {
        if (chatMessageService == null) {
            // 如果chatMessageService为空，则从Spring容器中获取
            EndpointConfig config = (EndpointConfig) session.getUserProperties().get("javax.websocket.endpoint.config");
            HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
            WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(httpSession.getServletContext());
            chatMessageService = context.getBean(IChatMessageService.class);
        }
        
        // 更新状态
        chatMessageService.updateMessageSendStatus(messageId, sendStatus);
    }
    
    /**
     * 获取在线用户列表
     */
    public static String[] getOnlineUsers() {
        return clients.keySet().toArray(new String[0]);
    }
    
    /**
     * 检查用户是否在线
     */
    public static boolean isUserOnline(String userId) {
        return clients.containsKey(userId);
    }
    
    /**
     * 广播消息给所有用户
     */
    public static void broadcastMessage(String message) {
        clients.forEach((userId, client) -> {
            try {
                client.session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                log.error("广播消息发送失败", e);
            }
        });
    }
} 